Add docs on 'Create app flavors for Windows and Linux' - #13736
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new documentation guide for setting up Flutter flavors on Windows and Linux desktop apps, along with updating existing docs and navigation links to reference it. Feedback on the new guide highlights a critical MDX syntax error with an unclosed <Tabs> tag, and suggests providing more concrete, actionable CMake and configuration examples for customizing application icons on both Windows and Linux.
|
Staged preview of the updated docs.flutter.dev site (updated for commit 2fbd18f): https://flutter-docs-prod--docs-pr13736-flavors-windows-linux-5airv7fi.web.app |
|
@AngeloAvv, can you help with a review? The bot is suggesting a more detailed description and I could use some help. |
|
Staged preview of the updated flutter.dev site (updated for commit 2fbd18f): https://flutter-dev-230821--www-pr13736-flavors-windows-linux-ljw8my0n.web.app |
|
@sfshaza2 sure! I'll take a look in the upcoming days |
|
This PR is holding up other PRs. @AngeloAvv, unless you can get to this soon-ish, I plan to land, because these changes are better than nothing. But in that event, I would still really appreciate if you could either file an issue or a PR with improvements! |
parlough
left a comment
There was a problem hiding this comment.
The fact that there's a set of tabs under most of the sections and a table to visual locations by platform, I'd highly recommend splitting this into two pages. It seems there's not much relating the platforms to each other in the case of flavors, so I think it's best for developers to tackle each separately anyway.
| 1. Create a new Flutter project called `flavors_example`: | ||
|
|
||
| ```console title="console" | ||
| $ flutter create flavors_example | ||
| $ cd flavors_example | ||
| ``` | ||
|
|
||
| 1. Configure the CMake build files for your target platform: | ||
|
|
||
| <Tabs key="flavors-os" wrapped="true"> |
There was a problem hiding this comment.
Markdown list nesting looks off and will likely break the numbering in rendering.
Item 1 (line 80) uses 1. + 4-space continuation indent, but items at lines 87 and 126 use 1. with no indented body, and the <Tabs> blocks between them start at column 0. That takes the following content out of the list context, so the ordered list restarts rather than continuing 1 → 2 → 3.
Same pattern in the "Create distinct window titles" section (lines 271/280 and 299/308).
Compare with flavors-ios.md, which keeps everything under a consistent 4-space continuation indent. Worth previewing the built page to confirm the step numbers render as 1/2/3.
| 1. Open `linux/runner/CMakeLists.txt` and pass `FLUTTER_APP_FLAVOR` | ||
| as a preprocessor definition: | ||
|
|
||
| ```cmake title="linux/runner/CMakeLists.txt" | ||
| if(DEFINED FLUTTER_APP_FLAVOR AND NOT FLUTTER_APP_FLAVOR STREQUAL "") | ||
| target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_APP_FLAVOR=\"${FLUTTER_APP_FLAVOR}\"") | ||
| endif() | ||
| ``` | ||
|
|
||
| 1. Open `linux/runner/my_application.cc` and update the window title | ||
| in the `my_application_activate` function: | ||
|
|
||
| ```c title="linux/runner/my_application.cc" | ||
| #if defined(FLUTTER_APP_FLAVOR) | ||
| const char* title = "flavors_example (" FLUTTER_APP_FLAVOR ")"; | ||
| #else | ||
| const char* title = "flavors_example"; | ||
| #endif | ||
|
|
||
| if (use_header_bar) { | ||
| GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); | ||
| gtk_widget_show(GTK_WIDGET(header_bar)); | ||
| gtk_header_bar_set_title(header_bar, title); | ||
| gtk_header_bar_set_show_close_button(header_bar, TRUE); | ||
| gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); | ||
| } else { | ||
| gtk_window_set_title(window, title); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
The Linux mechanism here is correct — unlike the Windows tab. GTK takes narrow const char* strings, so the preprocessor define works directly with no conversion. flutter_flavorizr does exactly this, and its example project builds and runs with it.
Two adjustments to match what actually works:
1. Placement. target_compile_definitions(${BINARY_NAME} …) needs the target to exist, so it must come after add_executable(${BINARY_NAME} …). flutter_flavorizr anchors on the existing APPLICATION_ID define, which is already correctly positioned:
# Add preprocessor definitions for the application ID.
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
if(DEFINED FLUTTER_APP_FLAVOR AND NOT FLUTTER_APP_FLAVOR STREQUAL "")
add_definitions(-DFLUTTER_APP_FLAVOR="${FLUTTER_APP_FLAVOR}")
endif()Placing the new block right after the APPLICATION_ID line is an easy instruction to follow and is guaranteed to be in scope.
2. my_application.cc snippet doesn't match the file it's editing. The generated my_application_activate passes the title as a string literal in two places. The snippet introduces a title variable but shows the if (use_header_bar) block dedented to column 0, whereas in the real file it's indented two spaces inside the function — so this can't be pasted as-is.
The working shape, which keeps the diff to the existing code minimal:
const gchar* window_title = "flavors_example";
#ifdef FLUTTER_APP_FLAVOR
if (g_strcmp0(FLUTTER_APP_FLAVOR, "staging") == 0) {
window_title = "Staging App";
} else if (g_strcmp0(FLUTTER_APP_FLAVOR, "production") == 0) {
window_title = "Production App";
}
#endifthen replace the two hardcoded literals with window_title:
gtk_header_bar_set_title(header_bar, window_title);
...
gtk_window_set_title(window, window_title);Using g_strcmp0 rather than string concatenation also lets each flavor have a fully custom title instead of AppName (flavor), which is closer to what people actually want from flavors.
| 1. Prepare your icon files in `.ico` format | ||
| (for example, `app_icon_staging.ico` and `app_icon_production.ico`) | ||
| and place them in `windows/runner/resources/`. | ||
|
|
||
| 1. In `windows/CMakeLists.txt`, select the appropriate icon file | ||
| based on `FLUTTER_APP_FLAVOR`: | ||
|
|
||
| ```cmake title="windows/CMakeLists.txt" | ||
| if(FLUTTER_APP_FLAVOR STREQUAL "staging") | ||
| set(APP_ICON_NAME "app_icon_staging.ico") | ||
| elseif(FLUTTER_APP_FLAVOR STREQUAL "production") | ||
| set(APP_ICON_NAME "app_icon_production.ico") | ||
| else() | ||
| set(APP_ICON_NAME "app_icon.ico") | ||
| endif() | ||
| ``` | ||
|
|
||
| 1. Configure `windows/runner/Runner.rc` or your CMake target | ||
| to use `APP_ICON_NAME` for the application icon resource. |
There was a problem hiding this comment.
Blocking — this section isn't implementable as written.
Two problems:
-
The snippet targets
windows/CMakeLists.txt, whereFLUTTER_APP_FLAVORis still undefined (same scope issue as the earlier section).if(FLUTTER_APP_FLAVOR STREQUAL "staging")on an undefined variable is always false, soAPP_ICON_NAMEsilently falls through toelse(). -
Step 3 — "Configure
windows/runner/Runner.rcor your CMake target to useAPP_ICON_NAME" — is the entire difficulty, and it's left as an exercise.Runner.rcis a resource script; it cannot read CMake variables. There is no way to complete this step from what the page provides.
flutter_flavorizr handles this with the same configure_file pass proposed in my comment on the window-title section — one mechanism covering icon, title, and PE metadata. Extend that block in windows/runner/CMakeLists.txt, before add_executable:
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/Runner.rc.in"
"${CMAKE_CURRENT_SOURCE_DIR}/Runner.rc"
@ONLY
)Rename Runner.rc to Runner.rc.in and parameterize it — these are the exact substitutions its example project ships:
IDI_APP_ICON ICON "resources\\@RUNNER_APP_ICON@"
VALUE "FileDescription", "@WINDOW_TITLE@" "\0"
VALUE "ProductName", "@WINDOW_TITLE@" "\0"
with the per-flavor .ico files sitting in windows/runner/resources/ alongside the default app_icon.ico.
Same caveat as before: Runner.rc becomes a generated file, so it should be gitignored while Runner.rc.in is committed.
| 1. Prepare PNG icons for each flavor | ||
| (for example, `app_icon_staging.png` and `app_icon_production.png`). | ||
|
|
||
| 1. Create corresponding `.desktop` files for each flavor | ||
| (for example, `flavors_example_staging.desktop` and | ||
| `flavors_example_production.desktop`) | ||
| that reference the respective icon and executable binary name. | ||
|
|
||
| 1. Update `linux/CMakeLists.txt` to install the correct `.desktop` file | ||
| and icon based on `FLUTTER_APP_FLAVOR`. |
There was a problem hiding this comment.
This section is prose-only, and step 3 — "Update linux/CMakeLists.txt to install the correct .desktop file and icon" — is where all the actual work lives. A reader can't act on it.
It's also genuinely harder than the Windows case: on Linux the icon isn't embedded in the binary, so it depends on .desktop entries, the icon theme layout, and the packaging format (snap/deb/flatpak).
I'd suggest cutting these three steps and linking to Build and release a Linux app instead, noting that per-flavor icons are a packaging concern rather than a flavor concern. Shipping three non-actionable bullets sets readers up to fail — and I'd rather not propose a concrete recipe here, since I don't have a verified working example to base one on.
| ## Launch a flavor {: #launch-a-flavor } | ||
|
|
||
| After you configure flavors for your Windows or Linux app, |
There was a problem hiding this comment.
Two additions worth making somewhere in this section:
1. --flavor also works with flutter test and flutter drive on desktop, not just run/build. The Linux PR touched commands/test.dart for exactly this, and both devicelab tasks (flavors_test_linux.dart, flavors_test_windows.dart) exercise createFlavorsTest() / createIntegrationTestFlavorsTest(). Readers wiring flavors into CI will look for this.
2. Existing projects shouldn't need to regenerate anything. Neither desktop flavor PR modified the windows.tmpl / linux.tmpl templates, so --flavor works on any existing project as-is — no flutter create . re-run, no template migration. That's a genuinely reassuring detail and cheap to state.
| Replace `<flavor_name>` with the name of your flavor | ||
| (for example, `staging` or `production`). |
There was a problem hiding this comment.
This sentence sits inside the Linux tab, so readers on the Windows tab never see it — even though the Windows snippet above uses the same <flavor_name> placeholder.
Move it out of the <Tabs> block so it applies to both.
Closes #13642
Adds documentation for setting up app flavors on Windows and Linux desktop platforms.